/*******************************************************************************
 * Copyright 2008 www.softao.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
/**
 * Place holder for copyright, needs to be replaced before release.
 */
package com.softao.ming.gwt.drawing2d.client.ui.impl;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.user.client.Element;

/**
 * Implementation based on the VML.
 */
public class GraphicsImplVml extends GraphicsImpl {
	static class State {
		static State get(GraphicsImplVml instance) {
			State result = new State();
			result.mFillStyle = instance.mFillStyle;
			result.mStrokeStyle = instance.mStrokeStyle;
			result.mOffsetX = instance.mOffsetX;
			result.mOffsetY = instance.mOffsetY;
			result.mGlobalAlpha = instance.mGlobalAlpha;
			result.mLineWidth = instance.mLineWidth;
			result.mRotation = instance.mRotation;
			result.mPath = instance.mPath;
			
			return result;
		}
		
		public void put(GraphicsImplVml instance) {
			instance.mFillStyle = this.mFillStyle;
			instance.mStrokeStyle = this.mStrokeStyle;
			instance.mOffsetX = this.mOffsetX;
			instance.mOffsetY = this.mOffsetY;
			instance.mGlobalAlpha = this.mGlobalAlpha;
			instance.mLineWidth = this.mLineWidth;
			instance.mRotation = this.mRotation;
			instance.mPath = this.mPath;
		}
		
		private String mFillStyle;
		
		private String mStrokeStyle = "black";
		
		private float mOffsetX;
		
		private float mOffsetY;
		
		private float mGlobalAlpha = 1.0F;
		
		private float mLineWidth = 0F;
		
		private float mRotation = 0F;
		
		private String mPath = "";
	}
	
	Element mElement;
	
	private List<State> mStates = new ArrayList<State>();
	
	private String mFillStyle;
	
	private String mStrokeStyle = "black";
	
	private float mOffsetX;
	
	private float mOffsetY;
	
	private float mGlobalAlpha = 1.0F;
	
	private float mLineWidth = 0F;
	
	private float mRotation = 0F;
	
	private String mPath = "";
	
	@Override
	public void save() {
		this.mStates.add(State.get(this));
	}
	
	@Override
	public void restore() {
		if (this.mStates.size() == 0) {
			return;
		}
		State state = this.mStates.get(this.mStates.size() - 1);
		this.mStates.remove(this.mStates.size() - 1);
		state.put(this);
	}
	
	@Override
	public void initialize(Element element) {
		super.initialize(element);
		this.mElement = element;
		this.mFillStyle = "black";
	}
	
	/**
	 * @return the element
	 */
	public Element getElement() {
		return mElement;
	}
	
	@Override
	public void setFillStyle(final String color) {
		this.mFillStyle = color;
	}
	
	@Override
	public void setStrokeStyle(final String value) {
		mStrokeStyle = value;
	}
	
	@Override
	public native void fillRect(float x, float y, float w, float h) /*-{
		var o, f, element;
		element = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mElement;
		
//		if ((x == 0) && (y == 0) && (w == element.offsetWidth) && (h == element.offsetHeight) && (this._offsetX == 0) && (this._offsetY == 0) && (this.globalAlpha == 1)) {
//			while (element.firstChild) { element.removeChild(element.lastChild); }
//		}
		o = element.ownerDocument.createElement('vml:rect');
  		o.fillColor = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mFillStyle;
		o.filled = true;
		o.stroked = false;
		o.weight = 0;
		o.style.position = 'absolute';
		o.style.left = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mOffsetX + x;
		o.style.top = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mOffsetY + y;
		o.style.width = w;
		o.style.height = h;
		o.style.antialias = 'false';

		f = element.ownerDocument.createElement('vml:fill');
		f.opacity = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mGlobalAlpha;
		o.appendChild(f);

		element.appendChild(o);
	}-*/;
	
	@Override
	public native void strokeRect(float x, float y, float width, float height) /*-{
		var o, s, element;
		element = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mElement;

		o = element.ownerDocument.createElement('vml:rect');
		o.fillColor = 'none';
		o.filled = false;
		o.strokeColor = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mStrokeStyle;
		o.stroked = true;
		o.weight = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mLineWidth;
		o.style.position = 'absolute';
		o.style.left = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mOffsetX + x;
		o.style.top = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mOffsetY + y;
		o.style.width = width;
		o.style.height = height;
		
		s = element.ownerDocument.createElement('vml:fill');
		s.opacity = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mGlobalAlpha;
		o.appendChild(s);
		
		element.appendChild(o);
	}-*/;
	
	@Override
	public void beginPath() {
		this.mPath = "";
	}
	
	@Override
	public void setLineWidth(float value) {
		this.mLineWidth = value;
	}
	
	@Override
	public void moveTo(float x, float y) {
		this.mPath += " m" + Math.round(x) + "," + Math.round(y);
	}
	
	@Override
	public void lineTo(float x, float y) {
		this.mPath += " l" + Math.round(x) + "," + Math.round(y);
	}
	
	@Override
	public void quadraticCurveTo(float cpx, float cpy, float x, float y) {
		this.mPath += " qb" + Math.round(cpx) + "," + Math.round(cpy) + "," + Math.round(x) + "," + Math.round(y);
	}
	
	/* (non-Javadoc)
	 * @see com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImpl#bezierCurveTo(float, float, float, float, float, float)
	 */
	@Override
	public void bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y,
			float x, float y) {
		this.mPath += " qb" + Math.round(cp1x) + "," + Math.round(cp1y) + "," + Math.round(cp2x) + "," + Math.round(cp2y) + "," + Math.round(x) + "," + Math.round(y);
	}
	
	@Override
	public void rect(float x, float y, float w, float h) {
		int x1, y1, x2, y2;

		x1 = Math.round(x);
		y1 = Math.round(y);
		x2 = Math.round(x + w);
		y2 = Math.round(y + h);

		this.mPath += " m" + x1 + "," + y1;
		this.mPath += " l" + x2 + "," + y1;
		this.mPath += " l" + x2 + "," + y2;
		this.mPath += " l" + x1 + "," + y2;
		this.mPath += " x";
	}
	
	@Override
	public void arc(float x, float y, float radius, float startAngle,
			float endAngle, boolean anticlockwise) {
		float xi, yi, x1, y1, x2, y2, x3, y3, x4, y4;

		xi = x;
		yi = y;

		x1 = xi - radius;
		y1 = yi - radius;

		x2 = xi + radius;
		y2 = yi + radius;

		x3 = (float)(xi + (Math.cos(startAngle) * radius));
		y3 = (float)(yi + (Math.sin(startAngle) * radius));

		x4 = (float)(xi + (Math.cos(endAngle) * radius));
		y4 = (float)(yi + (Math.sin(endAngle) * radius));

		this.mPath += " ar" + Math.round(x1) + "," + Math.round(y1) + "," + Math.round(x2) + "," + Math.round(y2) + "," + Math.round(x3) + "," + Math.round(y3) + "," + Math.round(x4) + "," + Math.round(y4);
	}
	
	@Override
	public void closePath() {
		this.mPath += " x";
	}
	
	@Override
	public native void stroke() /*-{
		var o, s, cosa, sina, cx, cy, x, y, element;
		
		element = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mElement;

		if (!this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mPath) { return; }

		this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mPath += ' e';

		o = element.ownerDocument.createElement('vml:shape');
		o.fillColor = 'none';
		o.filled = false;
		o.strokeColor = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mStrokeStyle;
		o.stroked = true;
		o.weight = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mLineWidth;
		o.coordsize = element.offsetWidth + ',' + element.offsetHeight;
		o.style.position = 'absolute';
		o.style.left = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mOffsetX;
		o.style.top = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mOffsetY;
		o.style.width = element.offsetWidth;
		o.style.height = element.offsetHeight;
		o.path = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mPath;
		
		s = element.ownerDocument.createElement('vml:stroke');
		s.opacity = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mGlobalAlpha;
		o.appendChild(s);

		if (this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mRotation) {
			r = element.ownerDocument.createElement('vml:group');
			r.style.position = 'absolute';
			r.style.left = 0;
			r.style.top = 0;
			r.style.width = element.offsetWidth;
			r.style.height = element.offsetHeight;
			r.coordsize = o.coordsize;
			r.style.rotation = Math.round((this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mRotation * 180) / Math.PI);
			r.style.rotationCenter = '0,0';
			r.appendChild(o);
			element.appendChild(r);

			cosa = Math.cos(this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mRotation);
			sina = Math.sin(this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mRotation);
			cx = element.offsetWidth / 2;
			cy = element.offsetHeight / 2;

			x = ( cx*(1-cosa) + cy*sina);
			y = (-cx*sina     + cy*(1-cosa));

			r.style.left = x * -1;
			r.style.top = y * -1;
		}
		else { element.appendChild(o); }
	}-*/;
	
	@Override
	public native void fill() /*-{
		var o, f, r, element;

		element = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mElement;
		
		if (!this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mPath) { return; }

		this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mPath += ' e';

		o = element.ownerDocument.createElement('vml:shape');
		o.fillColor = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mFillStyle;
		o.strokeColor = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mStrokeStyle;
		o.stroked = false;
		o.weight = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mLineWidth;
		o.coordsize = element.offsetWidth + ',' + element.offsetHeight;
		o.style.position = 'absolute';
		o.style.left = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mOffsetX;
		o.style.top = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mOffsetY;
		o.style.width = element.offsetWidth;
		o.style.height = element.offsetHeight;
		o.path = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mPath;

		f = element.ownerDocument.createElement('vml:fill');
		f.opacity = this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mGlobalAlpha;
		o.appendChild(f);

		if (this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mRotation) {
			r = element.ownerDocument.createElement('vml:group');
			r.style.position = 'absolute';
			r.style.left = 0;
			r.style.top = 0;
			r.style.width = element.offsetWidth;
			r.style.height = element.offsetHeight;
			r.coordsize = o.coordsize;
			r.style.rotation = Math.round((this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mRotation * 180) / Math.PI);
			r.style.rotationCenter = '0,0';
			r.appendChild(o);
			element.appendChild(r);

			cosa = Math.cos(this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mRotation);
			sina = Math.sin(this.@com.softao.ming.gwt.drawing2d.client.ui.impl.GraphicsImplVml::mRotation);
			cx = (element.offsetWidth) / 2;
			cy = (element.offsetHeight) / 2;
			x = ( cx*(1-cosa) + cy*sina);
			y = (-cx*sina     + cy*(1-cosa));

			r.style.left = x * -1;
			r.style.top = y * -1;
		}
		else { 
			element.appendChild(o); 
		}
	}-*/;
	
	@Override
	public void translate(float x, float y) {
		this.mOffsetX += x;
		this.mOffsetY += y;
	}
	
	@Override
	public void setGlobalAlpha(float value) {
		this.mGlobalAlpha = value;
	}
	
	@Override
	public void rotate(float angle) {
		float MAX = (float)Math.PI * 2;

		this.mRotation += angle;
		
		while (this.mRotation > MAX) { 
			this.mRotation = MAX - this.mRotation; 
		}
	}
}
